#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "../common.h"
extern "C" {
#include "../yy.h"
}
#include "../sizer.h"
#include "../pcoder.h"
#include "../display.h"
#include "../const.h"
//#include "../option.h"
#include "pic16e.h"
#include "pic16e_inst.h"
#include "pic16e_asm.h"

PIC16E :: PIC16E(char *out_file, Nlist *_nlist, Pcoder *_pcoder)
{
	memset(this, 0, sizeof(PIC16E));	// clean up the class data

	if( out_file ) fout = fopen(out_file, "w");
	if ( fout == NULL )	fout = stdout;
	asm16e = new P16E_ASM(fout);
	nlist  = _nlist;
	pcoder = _pcoder;

	accSave   = 4;
	isrStackSet = true;
}

PIC16E :: ~PIC16E()
{
	fclose(fout);
	delete asm16e;
}

////////////////////////////////////////////////////////////////////////////////////////
void PIC16E :: run(void)
{
	time_t t = time (&t);	// current time
	char *buf = STRBUF();	// string buffer
	Nnode *nnp = NULL;
	int ram_size = 0;
	int stack_addr = 0;

    sprintf(buf, "\t.option nopic\n"
        "\t.attribute arch, \"rv32i2p1_m2p0\"\n"
        "\t.attribute unaligned_access, 0\n"
        "\t.attribute stack_align, 16\n"
        "\t.text\n"
		 "\t.global main\n");
	ASM_OUTP(buf);


	if ( stack_addr > 0 )
	{
		sprintf(buf, "%s\t0x%X\t; stack init. value\n", _EQU, stack_addr);
		ASM_LABL((char*)"_$$", true, buf);
	}

	// allocate memory for fixed address & public data
	//if ( outputData(dataLink) )	ASM_OUTP("\n");
	
	// generate init code
	outputInit(pcoder->initPcode);

	// generate constant code
	outputConst(pcoder->constPcode);

	// output constant strings
	outputString(pcoder->constGroup->list);

	// generate main program ASM code
	outputASM0(pcoder->mainPcode);


	ASM_OUTP("\n");
	ASM_CODE(_END);
}

void PIC16E :: errPrint(const char *msg)
{
	if ( srcCode )
		printf("%s - %s\n", msg, srcCode);
	else
		printf ("%s\n", msg);
	errors++;
}

int PIC16E :: outputData(Dlink *dlink)
{
	if ( dlink == NULL ) return 0;
	int count = 0;
	char *buf = STRBUF();

	for (Dnode *dnode = dlink->dlist; dnode; dnode = dnode->next)
	{
		attrib *attr = dnode->attr;
		if ( attr == NULL || attr->isExtern ) continue;

		int bank = attr->dataBank;
		if ( bank == CONST ) continue;


		sprintf(buf, "%s\t%d", (char*)".word", 1);
		ASM_LABL(dnode->nameStr(), !attr->isStatic, buf);
		count++;
	}
	return count;
}

void PIC16E :: outputASM0(Pnode *plist)
{
	for(; plist; plist = plist->next)
	{
		curPnode = plist;
		int pcode = plist->type;
		Item *ip0 = plist->items[0];
		Item *ip1 = plist->items[1];
		Item *ip2 = plist->items[2];

		switch ( pcode )
		{
			case P_FUNC_BEG:	// function begin
				curFunc = (Fnode*)ip0->val.p;
				if ( curFunc->endLbl > 0 )	// it's func. definition
					funcBeg(plist);
				else
					curFunc = NULL;
				break;

			case P_FUNC_END:
				curFunc = (Fnode*)ip0->val.p;
				if ( curFunc->endLbl > 0 )	// it's func. definition
					funcEnd();
				curFunc = NULL;
				break;

			case P_SRC_CODE:	// source code insertion
				if ( curFunc )
				{
					srcCode = ip0->val.s;
					//ASM_OUTP("# :: "); ASM_OUTP(srcCode); ASM_OUTP("\n");
				}
				break;

			case LABEL:			// label
				ASM_LABL(ip0->val.s);
				break;

			case CALL:
				call(ip0->val.s);
				break;

			case GOTO:
				asm16e->code(_J, ip0->val.s);
				break;

			case '=':	case P_MOV:
				mov(ip0, ip1);
				break;

			case INC_OP:	case DEC_OP:
				incValue(ip0, (pcode == INC_OP)? ip1->val.i: -ip1->val.i);
				break;

			case NEG_OF:
				break;

			case '~':
				//compl1(ip0, ip1);
				break;

			case '!':
				//notop(ip0, ip1);
				break;

			case ADD_ASSIGN:
			case SUB_ASSIGN:
				break;

			case AND_ASSIGN:
			case OR_ASSIGN:
			case XOR_ASSIGN:
				break;

			case P_JZ:	case P_JNZ:	// variable test
				//jzjnz(pcode, ip0, ip1);
				break;

			case P_JBZ:	case P_JBNZ:// bit test
				break;

			case P_ARG_PASS:
				passarg(ip0, ip1, ip2);
				break;

			case P_JZ_INC:	case P_JZ_DEC: 	case P_JNZ_INC:	case P_JNZ_DEC:
				break;

			case '+':
				add(ip0, ip1, ip2);
				break;
			case '-':
				sub(ip0, ip1, ip2);
				break;

			case P_JEQ:	case P_JNE:
				jeqjne(pcode, ip0, ip1, ip2);
				break;

			case P_JLT: case P_JLE:	case P_JGT:	case P_JGE:
				cmpJump(pcode, ip0, ip1, ip2);
				break;

			case LEFT_ASSIGN:
				leftAssign(ip0, ip1);
				break;

			case RIGHT_ASSIGN:
				rightAssign(ip0, ip1, ip0->acceSign());
				break;

			case LEFT_OP:
				leftOpr(ip0, ip1, ip2);
				break;
			case RIGHT_OP:
				rightOpr(ip0, ip1, ip2);
				break;

			case MUL_ASSIGN:
				mulAssign(ip0, ip1);
				break;

			case '*':
				mul(ip0, ip1, ip2);
				break;

			case DIV_ASSIGN:
			case MOD_ASSIGN:
				break;

			case '/':
				div(pcode, ip0, ip1, ip2);
				break;
			case '%':
				mod(pcode, ip0, ip1, ip2);
				break;

			case P_DJNZ: // dec, jp on NZ
			case P_IJNZ: // inc, jp on Z
				djnz(pcode, ip0, ip1);
				break;

			case P_ARG_CLEAR:
				ASM_CODE(_ADDFSR, 1, ip0->val.i);
				break;

			case '&':
			case '|':
			case '^':
				andorxor(pcode, ip0, ip1, ip2);
				break;

			default:
				if ( pcode != ';' )
				{
					char *buf = STRBUF();
					sprintf(buf, "unknown p-code: '%d'!\n", pcode);
					errPrint(buf);
				}
				break;
		}
	}
}
